<?php  if ( ! defined('BASEPATH')) exit('禁止直接访问脚本目录！');
/**
 * CodeIgniter
 *
 * 这是一个PHP开源程序开发框架，要求PHP 5.1.6+
 *
 * @package		CodeIgniter
 * @author		ExpressionEngine Dev Team
 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
 * @license		http://codeigniter.com/user_guide/license.html
 * @link		http://codeigniter.com
 * @since		Version 1.0
 * @filesource
 */

// ------------------------------------------------------------------------

/**
 * 加载器类
 *
 * 加载视图及文件
 *
 * @package		CodeIgniter
 * @subpackage	Libraries
 * @author		ExpressionEngine Dev Team
 * @category	Loader
 * @link		http://codeigniter.com/user_guide/libraries/loader.html
 */
class CI_Loader {

	// 这个类的东西都是自动搞定的，不需要你来干预。
	/**
	 * 缓冲输出机制的嵌套级别
	 *
	 * @var int
	 * @access protected
	 */
	protected $_ci_ob_level;
	/**
	 * 要被加载的视图路径名单
	 *
	 * @var array
	 * @access protected
	 */
	protected $_ci_view_paths		= array();
	/**
	 * 要被加载的库的名单
	 *
	 * @var array
	 * @access protected
	 */
	protected $_ci_library_paths	= array();
	/**
	 * 要被加载的模型的名单
	 *
	 * @var array
	 * @access protected
	 */
	protected $_ci_model_paths		= array();
	/**
	 * 要被加载的辅助文件的名单
	 *
	 * @var array
	 * @access protected
	 */
	protected $_ci_helper_paths		= array();
	/**
	 * 已加载的基础类名单
	 *
	 * @var array
	 * @access protected
	 */
	protected $_base_classes		= array(); // 由控制器类设置
	/**
	 * 被缓存的变量名单
	 *
	 * @var array
	 * @access protected
	 */
	protected $_ci_cached_vars		= array();
	/**
	 * 已加载的类的名单
	 *
	 * @var array
	 * @access protected
	 */
	protected $_ci_classes			= array();
	/**
	 * 已加载的文件
	 *
	 * @var array
	 * @access protected
	 */
	protected $_ci_loaded_files		= array();
	/**
	 * 已加载的模型
	 *
	 * @var array
	 * @access protected
	 */
	protected $_ci_models			= array();
	/**
	 * 已加载的辅助
	 *
	 * @var array
	 * @access protected
	 */
	protected $_ci_helpers			= array();
	/**
	 * 类名映射名单
	 *
	 * @var array
	 * @access protected
	 */
	protected $_ci_varmap			= array('unit_test'  => 'unit',
											'user_agent' => 'agent');

	/**
	 * 构造函数
	 *
	 * 设置视图文件路径和获取初始输出缓冲级别
	 */
	public function __construct()
	{
		$this->_ci_ob_level  = ob_get_level();
		$this->_ci_library_paths = array(APPPATH, BASEPATH);
		$this->_ci_helper_paths = array(APPPATH, BASEPATH);
		$this->_ci_model_paths = array(APPPATH);
		$this->_ci_view_paths = array(APPPATH.'views/'	=> TRUE);

		log_message('debug', "加载器类已初始化完成");
	}

	// --------------------------------------------------------------------

	/**
	 * 初始化加载器
	 *
	 * 该方法会被CI_Controller调用一次。
	 *
	 * @param 	array
	 * @return 	object
	 */
	public function initialize()
	{
		$this->_ci_classes = array();
		$this->_ci_loaded_files = array();
		$this->_ci_models = array();
		$this->_base_classes =& is_loaded();

		$this->_ci_autoloader();

		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * 是否已加载
	 *
	 * 该函数用以测试一个类是否存在于self::$_ci_classes数组中。
	 * 如果该类已被加载则返回对象名称，否则返回FALSE。
	 *
	 * 主要用在form_helper -> _get_validation_object()
	 *
	 * @param 	string	被检查的类
	 * @return 	mixed	在CI超级对象职工的类对象名称或者是FALSE
	 */
	public function is_loaded($class)
	{
		if (isset($this->_ci_classes[$class]))
		{
			return $this->_ci_classes[$class];
		}

		return FALSE;
	}

	// --------------------------------------------------------------------

	/**
	 * 类加载器
	 *
	 * 该函数用来加载类和对类进行实例化。
	 * 该函数是在控制器中被调用的。
	 *
	 * @param	string	类名
	 * @param	mixed	可选参数
	 * @param	string	对象名称
	 * @return	void
	 */
	public function library($library = '', $params = NULL, $object_name = NULL)
	{
		if (is_array($library))
		{
			foreach ($library as $class)
			{
				$this->library($class, $params);
			}

			return;
		}

		if ($library == '' OR isset($this->_base_classes[$library]))
		{
			return FALSE;
		}

		if ( ! is_null($params) && ! is_array($params))
		{
			$params = NULL;
		}

		$this->_ci_load_class($library, $params, $object_name);
	}

	// --------------------------------------------------------------------

	/**
	 * 模型加载器
	 *
	 * 该函数允许用户加载和实例化模型。
	 *
	 * @param	string	类名
	 * @param	string	模型名
	 * @param	bool	连接数据库
	 * @return	void
	 */
	public function model($model, $name = '', $db_conn = FALSE)
	{
		if (is_array($model))
		{
			foreach ($model as $babe)
			{
				$this->model($babe);
			}
			return;
		}

		if ($model == '')
		{
			return;
		}

		$path = '';

		// 如果模型位于一个子文件夹中，那么则解析出相应的文件名和路径
		if (($last_slash = strrpos($model, '/')) !== FALSE)
		{
			// 路径为最后一个路径分隔符前面的部分
			$path = substr($model, 0, $last_slash + 1);

			// 模型名为最后一个路径分隔符后面的部分
			$model = substr($model, $last_slash + 1);
		}

		if ($name == '')
		{
			$name = $model;
		}

		if (in_array($name, $this->_ci_models, TRUE))
		{
			return;
		}

		$CI =& get_instance();
		if (isset($CI->$name))
		{
			show_error('正在加载模型的名称，是一个已经被使用的资源的名称: '.$name);
		}

		$model = strtolower($model);

		foreach ($this->_ci_model_paths as $mod_path)
		{
			if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
			{
				continue;
			}

			if ($db_conn !== FALSE AND ! class_exists('CI_DB'))
			{
				if ($db_conn === TRUE)
				{
					$db_conn = '';
				}

				$CI->load->database($db_conn, FALSE, TRUE);
			}

			if ( ! class_exists('CI_Model'))
			{
				load_class('Model', 'core');
			}

			require_once($mod_path.'models/'.$path.$model.'.php');

			$model = ucfirst($model);

			$CI->$name = new $model();

			$this->_ci_models[] = $name;
			return;
		}

		// 找不到模型
		show_error('无法找到你所指定的模型: '.$model);
	}

	// --------------------------------------------------------------------

	/**
	 * 数据库加载器
	 *
	 * @param	string	the DB credentials
	 * @param	bool	是否返回DB对象
	 * @param	bool	是否开启active record (这允许我们对配置设置进行重写)
	 * @return	object
	 */
	public function database($params = '', $return = FALSE, $active_record = NULL)
	{
		// 引用超级对象
		$CI =& get_instance();

		// 判断是否需要加载数据库类
		if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db))
		{
			return FALSE;
		}

		require_once(BASEPATH.'database/DB.php');

		if ($return === TRUE)
		{
			return DB($params, $active_record);
		}

		// 初始化db变量。
		$CI->db = '';

		// 加载数据库类
		$CI->db =& DB($params, $active_record);
	}

	// --------------------------------------------------------------------

	/**
	 * 加载工具类
	 *
	 * @return	string
	 */
	public function dbutil()
	{
		if ( ! class_exists('CI_DB'))
		{
			$this->database();
		}

		$CI =& get_instance();

		// 为了达到向下兼容的目的而加载dbforge，以后CI肯定会取消这个东西
		// 所以说强烈建议不要使用。
		$CI->load->dbforge();

		require_once(BASEPATH.'database/DB_utility.php');
		require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_utility.php');
		$class = 'CI_DB_'.$CI->db->dbdriver.'_utility';

		$CI->dbutil = new $class();
	}

	// --------------------------------------------------------------------

	/**
	 * 加载数据库伪造类
	 *
	 * @return	string
	 */
	public function dbforge()
	{
		if ( ! class_exists('CI_DB'))
		{
			$this->database();
		}

		$CI =& get_instance();

		require_once(BASEPATH.'database/DB_forge.php');
		require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge.php');
		$class = 'CI_DB_'.$CI->db->dbdriver.'_forge';

		$CI->dbforge = new $class();
	}

	// --------------------------------------------------------------------

	/**
	 * 加载视图
	 *
	 * 该函数用来加载视图文件。有三个参数：
	 *
	 * 1. 被加载的视图文件的名称
	 * 2. 携带数据的关联数组
	 * 3. TRUE/FALSE - 返回视图数据或者输出视图数据。(某些情况下我们可能希望对
	 * 调用的视图数据进行进一步的处理，而不是直接输出到浏览器，那么这个时候，我
	 * 们就需要将第三个参数设置为TRUE)
	 *
	 * @param	string
	 * @param	array
	 * @param	bool
	 * @return	void
	 */
	public function view($view, $vars = array(), $return = FALSE)
	{
		return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
	}

	// --------------------------------------------------------------------

	/**
	 * 加载文件
	 *
	 * 这是通用文件加载器
	 *
	 * @param	string
	 * @param	bool
	 * @return	string
	 */
	public function file($path, $return = FALSE)
	{
		return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
	}

	// --------------------------------------------------------------------

	/**
	 * 设置变量
	 *
	 * 变量一旦被设置，意味着该变量在控制器类及视图文件中都可以正常使用。
	 *
	 * @param	array
	 * @param 	string
	 * @return	void
	 */
	public function vars($vars = array(), $val = '')
	{
		if ($val != '' AND is_string($vars))
		{
			$vars = array($vars => $val);
		}

		$vars = $this->_ci_object_to_array($vars);

		if (is_array($vars) AND count($vars) > 0)
		{
			foreach ($vars as $key => $val)
			{
				$this->_ci_cached_vars[$key] = $val;
			}
		}
	}

	// --------------------------------------------------------------------

	/**
	 * 获取变量
	 *
	 * 检测变量是否被设置，是则取回。
	 *
	 * @param	array
	 * @return	void
	 */
	public function get_var($key)
	{
		return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
	}

	// --------------------------------------------------------------------

	/**
	 * 加载辅助文件1
	 *
	 * 该函数用以加载指定的辅助文件。
	 *
	 * @param	mixed
	 * @return	void
	 */
	public function helper($helpers = array())
	{
		foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper)
		{
			if (isset($this->_ci_helpers[$helper]))
			{
				continue;
			}

			$ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.'.php';

			// 判断该请求是否请求的是扩展的辅助文件。
			if (file_exists($ext_helper))
			{
				$base_helper = BASEPATH.'helpers/'.$helper.'.php';

				if ( ! file_exists($base_helper))
				{
					show_error('无法加载被请求的文件: helpers/'.$helper.'.php');
				}

				include_once($ext_helper);
				include_once($base_helper);

				$this->_ci_helpers[$helper] = TRUE;
				log_message('debug', '辅助文件已成功加载: '.$helper);
				continue;
			}

			// 尝试加载辅助文件
			foreach ($this->_ci_helper_paths as $path)
			{
				if (file_exists($path.'helpers/'.$helper.'.php'))
				{
					include_once($path.'helpers/'.$helper.'.php');

					$this->_ci_helpers[$helper] = TRUE;
					log_message('debug', 'Helper loaded: '.$helper);
					break;
				}
			}

			// 无法加载辅助文件
			if ( ! isset($this->_ci_helpers[$helper]))
			{
				show_error('无法加载被请求的文件: helpers/'.$helper.'.php');
			}
		}
	}

	// --------------------------------------------------------------------

	/**
	 * 加载辅助文件2
	 *
	 * 这个函数调用的还是上面那个函数，只是函数名一个是单数，一个是复数。这样做
	 * 你就不用纠结于该函数名到底哪个才正确的问题了。
	 *
	 * @param	array
	 * @return	void
	 */
	public function helpers($helpers = array())
	{
		$this->helper($helpers);
	}

	// --------------------------------------------------------------------

	/**
	 * 加载语言文件
	 *
	 * @param	array
	 * @param	string
	 * @return	void
	 */
	public function language($file = array(), $lang = '')
	{
		$CI =& get_instance();

		if ( ! is_array($file))
		{
			$file = array($file);
		}

		foreach ($file as $langfile)
		{
			$CI->lang->load($langfile, $lang);
		}
	}

	// --------------------------------------------------------------------

	/**
	 * 加载配置文件
	 *
	 * @param	string
	 * @param	bool
	 * @param 	bool
	 * @return	void
	 */
	public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
	{
		$CI =& get_instance();
		$CI->config->load($file, $use_sections, $fail_gracefully);
	}

	// --------------------------------------------------------------------

	/**
	 * 驱动
	 *
	 * 加载驱动库
	 *
	 * @param	string	类名
	 * @param	mixed	可选参数
	 * @param	string	对象名，可选
	 * @return	void
	 */
	public function driver($library = '', $params = NULL, $object_name = NULL)
	{
		if ( ! class_exists('CI_Driver_Library'))
		{
			// 我们不在此进行实例化操作，因为Library自己会完成这件事。
			require BASEPATH.'libraries/Driver.php';
		}

		if ($library == '')
		{
			return FALSE;
		}

		// 由于驱动总是位于子文件夹中，并且库的名字通常都是相同的，所以我们可以
		// 稍稍的判断一下，这样可以节约点时间。
		if ( ! strpos($library, '/'))
		{
			$library = ucfirst($library).'/'.$library;
		}

		return $this->library($library, $params, $object_name);
	}

	// --------------------------------------------------------------------

	/**
	 * 添加包路径
	 *
	 * 添加库、模型、辅助文件和配置文件的父路径到路径数组中
	 *
	 * @param	string
	 * @param 	boolean
	 * @return	void
	 */
	public function add_package_path($path, $view_cascade=TRUE)
	{
		$path = rtrim($path, '/').'/';

		array_unshift($this->_ci_library_paths, $path);
		array_unshift($this->_ci_model_paths, $path);
		array_unshift($this->_ci_helper_paths, $path);

		$this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths;

		// 添加配置文件路径
		$config =& $this->_ci_get_component('config');
		array_unshift($config->_config_paths, $path);
	}

	// --------------------------------------------------------------------

	/**
	 * 获取包路径
	 *
	 * 返回包路径数组，默认情况下不包含 BASEPATH。
	 *
	 * @param	string
	 * @return	void
	 */
	public function get_package_paths($include_base = FALSE)
	{
		return $include_base === TRUE ? $this->_ci_library_paths : $this->_ci_model_paths;
	}

	// --------------------------------------------------------------------

	/**
	 * 删除包路径
	 *
	 * 当指定的路径存在于库、模型和辅助路径数组中时，则对其进行删除。
	 * 如果没有指定需要删除的具体路径，那么则删除最近添加的路径。
	 *
	 * @param	type
	 * @param 	bool
	 * @return	type
	 */
	public function remove_package_path($path = '', $remove_config_path = TRUE)
	{
		$config =& $this->_ci_get_component('config');

		if ($path == '')
		{
			$void = array_shift($this->_ci_library_paths);
			$void = array_shift($this->_ci_model_paths);
			$void = array_shift($this->_ci_helper_paths);
			$void = array_shift($this->_ci_view_paths);
			$void = array_shift($config->_config_paths);
		}
		else
		{
			$path = rtrim($path, '/').'/';
			foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var)
			{
				if (($key = array_search($path, $this->{$var})) !== FALSE)
				{
					unset($this->{$var}[$key]);
				}
			}

			if (isset($this->_ci_view_paths[$path.'views/']))
			{
				unset($this->_ci_view_paths[$path.'views/']);
			}

			if (($key = array_search($path, $config->_config_paths)) !== FALSE)
			{
				unset($config->_config_paths[$key]);
			}
		}

		// 确保应用程序的默认路径还存在于数组中。
		$this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
		$this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
		$this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
		$this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
		$config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
	}

	// --------------------------------------------------------------------

	/**
	 * 加载器
	 *
	 * 该函数用于加载视图和文件。
	 * 在变量前统统添加上_ci_开头的前缀来区别不同的变量，避免变量之间的冲突。
	 * 
	 *
	 * @param	array
	 * @return	void
	 */
	protected function _ci_load($_ci_data)
	{
		// 设置默认的数据变量
		foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
		{
			$$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val];
		}

		$file_exists = FALSE;

		// 设置被请求文件的路径
		if ($_ci_path != '')
		{
			$_ci_x = explode('/', $_ci_path);
			$_ci_file = end($_ci_x);
		}
		else
		{
			$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
			$_ci_file = ($_ci_ext == '') ? $_ci_view.'.php' : $_ci_view;

			foreach ($this->_ci_view_paths as $view_file => $cascade)
			{
				if (file_exists($view_file.$_ci_file))
				{
					$_ci_path = $view_file.$_ci_file;
					$file_exists = TRUE;
					break;
				}

				if ( ! $cascade)
				{
					break;
				}
			}
		}

		if ( ! $file_exists && ! file_exists($_ci_path))
		{
			show_error('无法加载被请求的文件: '.$_ci_file);
		}

		// 这使得我们可以在控制器和模型函数内可以成功的使用
		// $this->load(视图文件等等) 的方式来加载任意的东西。

		$_ci_CI =& get_instance();
		foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
		{
			if ( ! isset($this->$_ci_key))
			{
				$this->$_ci_key =& $_ci_CI->$_ci_key;
			}
		}

		/*
		 * 提取和缓存变量
		 *
		 * 你可以用专用的$this->load_vars()来设置变量，或者通过该函数的第二个参数来设置。
		 * 这样我们才可能在被嵌入到一个视图中的另一个视图中访问这些变量。
		 */
		if (is_array($_ci_vars))
		{
			$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
		}
		extract($this->_ci_cached_vars);

		/*
		 * 缓冲输出
		 *
		 * 缓冲输出有两方面的原因：
		 * 1.速度的原因。缓冲输出可以显著的提升速度。
		 * 2.后期处理原因。比如说我们想要计算一个页面从程序开始处理数据到
		 * 输出给浏览器的前一刻，总共花费的时间的时候，我们就需要缓冲输出
		 * 以便可以在输出的前一刻计算出时间。
		 */
		ob_start();

		// 如果当前环境不支持PHP短标签，那么我们会在此处将短标签转换为
		// PHP标准的标签形式。

		if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
		{
			echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
		}
		else
		{
			include($_ci_path); // 使用include()而不是include_once()是考虑到，我们可能有加载多个同名视图文件的可能。
		}

		log_message('debug', 'File loaded: '.$_ci_path);

		// 如果要求返回数据，则做返回处理
		if ($_ci_return === TRUE)
		{
			$buffer = ob_get_contents();
			@ob_end_clean();
			return $buffer;
		}

		/*
		 * 冲刷缓冲区
		 *
		 * 为了实现视图的嵌套，我们必须对任何超过一个缓冲层的缓冲区域进行冲刷。
		 * 以保证视图能够被正确的加载和输出到浏览器。(注意：PHP对于缓冲层的冲刷
		 * 是从最里层开始的，也就是说从最后调用ob_start()的那一层开始逐层的向外
		 * 冲刷出里层的缓冲内容，直至冲刷到第一层，然后发送给浏览器。不过这只是
		 * 大致上的流程机制，缓冲处理的函数比较多，不同的服务器、PHP版本、浏览器
		 * 对缓冲都有着一定的影响，建议多看官方文档，并加以实验。)
		 * 
		 */
		if (ob_get_level() > $this->_ci_ob_level + 1)
		{
			ob_end_flush();
		}
		else
		{
			$_ci_CI->output->append_output(ob_get_contents());
			@ob_end_clean();
		}
	}

	// --------------------------------------------------------------------

	/**
	 * 类的加载
	 *
	 * 该函数用来加载被请求的类。
	 *
	 * @param	string	被加载的项目
	 * @param	mixed	额外参数
	 * @param	string	可选的对象名
	 * @return	void
	 */
	protected function _ci_load_class($class, $params = NULL, $object_name = NULL)
	{
		// 删除斜杠，并且取得类名。
		// 目录路径可以作为类名的一部分，但是最前面不能含有斜杠，所以首先删除前后可能
		// 出现的斜杠。
		$class = str_replace('.php', '', trim($class, '/'));

		// 我们通过查找斜杠来判断类名中是否包含了路径。
		$subdir = '';
		if (($last_slash = strrpos($class, '/')) !== FALSE)
		{
			// 分离出路径
			$subdir = substr($class, 0, $last_slash + 1);

			// 获取文件名
			$class = substr($class, $last_slash + 1);
		}

		// 我们分别用小写形式和首字母大写形式来验证文件是否存在。(Linux系统区分文件名大小写)
		foreach (array(ucfirst($class), strtolower($class)) as $class)
		{
			$subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php';

			// 判断这是否是对一个扩展类发出的请求(提示：上面的代码中加了扩展类前缀)
			if (file_exists($subclass))
			{
				$baseclass = BASEPATH.'libraries/'.ucfirst($class).'.php';

				if ( ! file_exists($baseclass))
				{
					log_message('error', "无法加载被请求的类: ".$class);
					show_error("无法加载被请求的类: ".$class);
				}

				// 检测一下该类是否已经被加载过了。
				if (in_array($subclass, $this->_ci_loaded_files))
				{
					// 在我们即将认定这是一个重复请求之前，我们先判断一下是否有一个
					// 自定义的对象名被提供。如果确实提供了，那么我们就返回一个新的
					// 对象实例给该请求。
					if ( ! is_null($object_name))
					{
						$CI =& get_instance();
						if ( ! isset($CI->$object_name))
						{
							return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
						}
					}

					$is_duplicate = TRUE;
					log_message('debug', $class." 此类已被加载过，自动忽略本次加载请求");
					return;
				}

				include_once($baseclass);
				include_once($subclass);
				$this->_ci_loaded_files[] = $subclass;

				return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
			}

			// 检索该文件并加载
			$is_duplicate = FALSE;
			foreach ($this->_ci_library_paths as $path)
			{
				$filepath = $path.'libraries/'.$subdir.$class.'.php';

				// 判断该文件是否存在
				if ( ! file_exists($filepath))
				{
					continue;
				}

				// 判断该类是否已被加载过
				if (in_array($filepath, $this->_ci_loaded_files))
				{
					// 在我们即将认定这是一个重复请求之前，我们先判断一下是否有一个
					// 自定义的对象名被提供。如果确实提供了，那么我们就返回一个新的
					// 对象实例给该请求。
					if ( ! is_null($object_name))
					{
						$CI =& get_instance();
						if ( ! isset($CI->$object_name))
						{
							return $this->_ci_init_class($class, '', $params, $object_name);
						}
					}

					$is_duplicate = TRUE;
					log_message('debug', $class." 此类已被加载过，自动忽略本次加载请求");
					return;
				}

				include_once($filepath);
				$this->_ci_loaded_files[] = $filepath;
				return $this->_ci_init_class($class, '', $params, $object_name);
			}

		}

		// 最后的尝试。尝试一下在一个与类名同名的子文件夹中加载该类。
		if ($subdir == '')
		{
			$path = strtolower($class).'/'.$class;
			return $this->_ci_load_class($path, $params);
		}

		// 如果程序执行到这里的话，意味着无法加载该类。
		if ($is_duplicate == FALSE)
		{
			log_message('error', "无法加载被请求的类: ".$class);
			show_error("无法加载被请求的类: ".$class);
		}
	}

	// --------------------------------------------------------------------

	/**
	 * 类的实例化
	 *
	 * @param	string
	 * @param	string
	 * @param	bool
	 * @param	string	可选对象
	 * @return	null
	 */
	protected function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL)
	{
		// 该类是否有一个与之关联的配置文件？注意：这些应当都是小写的形式
		if ($config === NULL)
		{
			// 调用配置类，以便通过配置类中的路径数组来加载相应的配置文件
			$config_component = $this->_ci_get_component('config');

			if (is_array($config_component->_config_paths))
			{
				// 一旦成功引入第一个文件就立即终止循环，这样才不会出现被覆盖的情况。
				foreach ($config_component->_config_paths as $path)
				{
					// 不同的系统对于文件名大小写的区别不一样。在这里我们将大小写形式统统尝试一遍，
					// 以便正确的加载我们想要的文件。
					if (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
					{
						include($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
						break;
					}
					elseif (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
					{
						include($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
						break;
					}
					elseif (file_exists($path .'config/'.strtolower($class).'.php'))
					{
						include($path .'config/'.strtolower($class).'.php');
						break;
					}
					elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).'.php'))
					{
						include($path .'config/'.ucfirst(strtolower($class)).'.php');
						break;
					}
				}
			}
		}

		if ($prefix == '')
		{
			if (class_exists('CI_'.$class))
			{
				$name = 'CI_'.$class;
			}
			elseif (class_exists(config_item('subclass_prefix').$class))
			{
				$name = config_item('subclass_prefix').$class;
			}
			else
			{
				$name = $class;
			}
		}
		else
		{
			$name = $prefix.$class;
		}

		// 类名是否存在
		if ( ! class_exists($name))
		{
			log_message('error', "不存在的类: ".$name);
			show_error("不存在的类: ".$class);
		}

		// 为类设置对象名称。如果有自定义的对象名被提供，那么就使用自定义的该类的对象名。
		$class = strtolower($class);

		if (is_null($object_name))
		{
			$classvar = ( ! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class];
		}
		else
		{
			$classvar = $object_name;
		}

		// 保存类名和对象名。
		$this->_ci_classes[$class] = $classvar;

		// 实例化该类
		$CI =& get_instance();
		if ($config !== NULL)
		{
			$CI->$classvar = new $name($config);
		}
		else
		{
			$CI->$classvar = new $name;
		}
	}

	// --------------------------------------------------------------------

	/**
	 * 自动加载器
	 *
	 * config/autoload.php文件中有一个数组，这个数组中的东西都会被自动加载。
	 *
	 * @param	array
	 * @return	void
	 */
	private function _ci_autoloader()
	{
		if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
		{
			include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
		}
		else
		{
			include(APPPATH.'config/autoload.php');
		}

		if ( ! isset($autoload))
		{
			return FALSE;
		}

		// 包的自动加载
		if (isset($autoload['packages']))
		{
			foreach ($autoload['packages'] as $package_path)
			{
				$this->add_package_path($package_path);
			}
		}

		// 加载自定义配置文件
		if (count($autoload['config']) > 0)
		{
			$CI =& get_instance();
			foreach ($autoload['config'] as $key => $val)
			{
				$CI->config->load($val);
			}
		}

		// 自动加载辅助文件和语言文件
		foreach (array('helper', 'language') as $type)
		{
			if (isset($autoload[$type]) AND count($autoload[$type]) > 0)
			{
				$this->$type($autoload[$type]);
			}
		}

		// 为了向下兼容而在此处做了一个小的调整。$autoload['core']是老版本中的用法了。
		if ( ! isset($autoload['libraries']) AND isset($autoload['core']))
		{
			$autoload['libraries'] = $autoload['core'];
		}

		// 加载库
		if (isset($autoload['libraries']) AND count($autoload['libraries']) > 0)
		{
			// 加载数据库驱动
			if (in_array('database', $autoload['libraries']))
			{
				$this->database();
				$autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
			}

			// 加载其他的所有库
			foreach ($autoload['libraries'] as $item)
			{
				$this->library($item);
			}
		}

		// 模型的自动加载
		if (isset($autoload['model']))
		{
			$this->model($autoload['model']);
		}
	}

	// --------------------------------------------------------------------

	/**
	 * Object to Array
	 *
	 * Takes an object as input and converts the class variables to array key/vals
	 *
	 * @param	object
	 * @return	array
	 */
	protected function _ci_object_to_array($object)
	{
		return (is_object($object)) ? get_object_vars($object) : $object;
	}

	// --------------------------------------------------------------------

	/**
	 * 获取一个指定的库或者模型
	 *
	 * @param 	string
	 * @return	bool
	 */
	protected function &_ci_get_component($component)
	{
		$CI =& get_instance();
		return $CI->$component;
	}

	// --------------------------------------------------------------------

	/**
	 * 文件名预备
	 *
	 * 该函数获取不带后缀的文件名，以使得这些文件被更加可靠的加载。
	 *
	 * @param	mixed
	 * @param 	string
	 * @return	array
	 */
	protected function _ci_prep_filename($filename, $extension)
	{
		if ( ! is_array($filename))
		{
			return array(strtolower(str_replace('.php', '', str_replace($extension, '', $filename)).$extension));
		}
		else
		{
			foreach ($filename as $key => $val)
			{
				$filename[$key] = strtolower(str_replace('.php', '', str_replace($extension, '', $val)).$extension);
			}

			return $filename;
		}
	}
}

/* End of file Loader.php */
/* Location: ./system/core/Loader.php */